from .models import Script, ScriptRunningHistory, ScriptRunningHosts
from django.core.files import File
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage
from django.conf import settings
from django.core.paginator import Paginator
from django.db.models import F
from django.utils import timezone
from zipfile import ZipFile
import shutil
import os

# 构建脚本仓库文件系统
fs = FileSystemStorage(location=settings.SCRIPT_ROOT_DIR)


def get_scripts(param: dict = {}):
    pageNumber = param.get('pageNumber')
    pageSize = param.get('pageSize')
    script_type = param.get('script_type')
    name = param.get('name')
    # 初始化分页数据
    page = dict(pageNumber=pageNumber, pageSize=pageSize,
                pageTotal=1, total=None)
    # 获取Sciprt所有实例
    script_list = Script.objects.all().filter(deleted_at=None)
    # 根据类型过滤
    if script_type:
        script_list = script_list.filter(script_type=script_type)
    if name:
        script_list = script_list.filter(name__contains=name)
    page['total'] = script_list.count()
    # 如果存在分页大小，则进行分页
    if pageSize:
        ptr = Paginator(script_list, int(pageSize))
        page['pageTotal'] = ptr.count
        if pageNumber:
            script_list = ptr.page(pageNumber).object_list
        else:
            page['pageNumber'] = 1
            script_list = ptr.page(1).object_list
    else:
        pageTotal = 1

    try:
        scripts = []
        for script in list(script_list):
            scripts.append(script.meta_dict)
        return dict(list=scripts, page=page)
    except Exception as e:
        raise e


def post_script(script_info):
    script = Script.create(script_info)
    if not script_info.get(
            'content_file'):
        raise Exception('No file or content upload.')

    try:
        script.save()
        script.refresh_from_db()
    except Exception as e:
        raise e
    # 根据租户ID，脚本类型，脚本id生成脚本存放路径，并根据脚本名称hash值打散存储
    script.script_path = str(
        script.tenant_id+'/' +
        script.script_type+'/' +
        str(hash(script.name) & 0xf)+'/' +
        str(script.pk)+'/'
    )
    try:
        save_script(str(script.pk), script.script_path, script_info.get(
            'content_file'), script_info.get('script_type'))
    except Exception as e:
        raise e
    script.save()

    result = script.meta_dict
    return result


def save_script(name: str, path: str, file: File, script_type: str):
    if not file:
        raise Exception('No file or content upload.')

    # 判断脚本类型
    if "Playbook" == script_type:
        try:
            file.name = name+".zip"
            path = fs.path(path)
            os.makedirs(path)
            with ZipFile(file) as myzip:
                myzip.extractall(path)
        except Exception as e:
            raise e
        if not fs.exists(path+"/main.yml"):
            raise Exception("Can not find main.yml.")
    else:
        name = path+name
        if fs.exists(name):
            raise Exception('file already exists', name)
        try:
            fs.save(name, file)
        except Exception as e:
            raise e
    return path


def delete_script(script_id: int):
    # 获取脚本信息
    try:
        script = Script.objects.get(pk=script_id)
    except Exception as e:
        raise e
    if script.deleted_at:
        raise Exception("Script has been already deleted.")
    # 如果脚本类型为playbook删除文件夹，否则删除文件
    if not script.script_path:
        raise Exception("Script path is Null")
    name = script.script_path+script.name
    path = fs.path(name)
    # 判断路径是否存在
    if fs.exists(name):
        try:
            if "Playbook" == script.script_type:
                shutil.rmtree(path)
            else:
                fs.delete(name)
        except Exception as e:
            raise e
    # 更新script表的delete_at字段时间
    try:
        script.deleted_at = timezone.now()
        script.save()
        return script.meta_dict
    except Exception as e:
        raise e


def get_script(script_id: int):
    try:
        script = Script.objects.get(pk=script_id)
        return script.meta_dict
    except Exception as e:
        raise e


def get_history(script_id: int):
    history = []
    historys = ScriptRunningHistory.objects.filter(script_id=script_id)
    for h in historys:
        hosts = ScriptRunningHosts.objects.filter(
            script_running_history_id=h.pk)
        his = dict(
            userName=h.user_name,
            startAt=str(h.started_at),
            endAt=str(h.end_at),
            hostCount=hosts.count(),
            successCount=hosts.filter(status='ok').count(),
            failedCount=hosts.filter(status='faild').count(),
            hosts=list(hosts.values())
        )
        history.append(his)
    return history


def update_script(script_id: int, script_file: File = None, **kwargs):
    try:
        script = Script.objects.get(pk=script_id)
    except Exception as e:
        raise e
    # 更新详情
    script.name = kwargs.get('name', script.name)
    script.desc = kwargs.get('desc', script.desc)
    script.time_out = kwargs.get('time_out', script.time_out)
    content = kwargs.get('content')
    content_file = kwargs.get('content_file')
    if content or content_file:
        if "Playbook" == script.script_type:
            script_file = content_file
        elif content:
            script_file = ContentFile(content, script.name)
        else:
            script_file = content_file
    if script_file:
        try:
            if fs.exists(script.script_path):
                shutil.rmtree(fs.path(script.script_path))
            path = save_script(script.name, script.script_path,
                               script_file, script.script_type)
            script.script_path=path
        except Exception as e:
            raise e
    try:
        script.save()
        return script.meta_dict
    except Exception as e:
        raise e
